#include "sysconfig.h"

WCHAR Unicode::aufstable[256];
UINT Unicode::fscodepage;

char* Unicode::uutf8(const WCHAR* s)
{
    char* d;
    int len;

    if (s == nullptr)
        return nullptr;
    len = WideCharToMultiByte(CP_UTF8, 0, s, -1, nullptr, 0, 0, FALSE);
    if (!len)
    {
        Logger::Write(L"uutf8 error: %s", s);
        return "";
    }
    d = xmalloc(char, len + 1);
    WideCharToMultiByte(CP_UTF8, 0, s, -1, d, len, 0, FALSE);
    return d;
}

WCHAR* Unicode::utf8u(const char* s)
{
    WCHAR* d;
    int len;

    if (s == nullptr)
        return nullptr;
    len = MultiByteToWideChar(CP_UTF8, 0, s, -1, nullptr, 0);
    if (!len)
    {
        Logger::Write(L"uutf8 error: %s", au(s));
        return L"";
    }
    d = xmalloc(WCHAR, len + 1);
    MultiByteToWideChar(CP_UTF8, 0, s, -1, d, len);
    return d;
}

char* Unicode::ua(const WCHAR* s)
{
    char* d;
    int len;

    if (s == nullptr)
        return nullptr;
    len = WideCharToMultiByte(CP_ACP, 0, s, -1, nullptr, 0, 0, FALSE);
    if (!len)
    {
        Logger::Write(L"uutf8 error: %s", s);
        return "";
    }
    d = xmalloc(char, len + 1);
    WideCharToMultiByte(CP_ACP, 0, s, -1, d, len, 0, FALSE);
    return d;
}

WCHAR* Unicode::au(const char* s)
{
    if (s == nullptr)
        return nullptr;

    int len = MultiByteToWideChar(CP_ACP, 0, s, -1, nullptr, 0);
    if (!len)
    {
        Logger::Write(L"uutf8 error: %s", au(s));
        return L"";
    }
    WCHAR* d = new WCHAR[len + 1];
    MultiByteToWideChar(CP_ACP, 0, s, -1, d, len);
    return d;
}

void Unicode::au_copy(WCHAR* dst, int maxlen, const char* src)
{
    dst[0] = 0;
    MultiByteToWideChar(CP_ACP, 0, src, -1, dst, maxlen);
}

void Unicode::ua_copy(char* dst, int maxlen, const WCHAR* src)
{
    dst[0] = 0;
    WideCharToMultiByte(CP_ACP, 0, src, -1, dst, maxlen, 0, FALSE);
}

char* Unicode::ua_fs(const WCHAR* s, int defchar)
{
    char def = 0;

    if (s == nullptr)
        return nullptr;
    BOOL dc = FALSE;
    int len = WideCharToMultiByte(fscodepage, WC_NO_BEST_FIT_CHARS, s, -1, nullptr, 0, &def, &dc);
    if (!len)
    {
        Logger::Write(L"uutf8 error: %s", s);
        return "";
    }
    char* d = new char[len + 1];
    dc = FALSE;
    WideCharToMultiByte(fscodepage, WC_NO_BEST_FIT_CHARS, s, -1, d, len, &def, &dc);
    if (dc)
    {
        def = 0;
        for (int i = 0; i < len; i++)
        {
            if (d[i] == 0 || (d[i] < 32 || (d[i] >= 0x7f && d[i] <= 0x9f)))
            {
                WCHAR s2[2];
                char d2[2];
                s2[0] = s[i];
                s2[1] = 0;
                if (defchar < 0)
                {
                    d2[0] = (char)s[i];
                    def = 0;
                }
                else
                {
                    def = (char)defchar;
                    d2[0] = (char)defchar;
                }
                WideCharToMultiByte(0, 0, s2, -1, d2, 1, defchar >= 0 ? &def : nullptr, defchar >= 0 ? &dc : nullptr);
                d[i] = d2[0];
            }
        }
    }
    return d;
}

void Unicode::ua_fs_copy(char* dst, int maxlen, const WCHAR* src, int defchar)
{
    char def = 0;

    if (src == nullptr)
    {
        if (dst != nullptr)
            if (maxlen > 0)
                dst[0] = 0;
        return;
    }

    BOOL dc = FALSE;
    int len = WideCharToMultiByte(fscodepage, WC_NO_BEST_FIT_CHARS, src, -1, dst, maxlen, &def, &dc);
    if (!len)
    {
        dst[0] = 0;
        return;
    }
    if (dc)
    {
        def = 0;
        for (int i = 0; i < len; i++)
        {
            if (dst[i] == 0)
            {
                WCHAR s2[2];
                char d2[2];
                s2[0] = src[i];
                s2[1] = 0;
                if (defchar < 0)
                {
                    d2[0] = (char)src[i];
                    def = 0;
                }
                else
                {
                    def = (char)defchar;
                    d2[0] = (char)defchar;
                }
                WideCharToMultiByte(0, (defchar >= 0 ? WC_NO_BEST_FIT_CHARS : 0), s2, -1, d2, 1, defchar >= 0 ? &def : nullptr, defchar >= 0 ? &dc : nullptr);
                dst[i] = d2[0];
            }
        }
    }
}

WCHAR* Unicode::au_fs(const char* s)
{
    int len = strlen(s);
    WCHAR* d = xmalloc(WCHAR, len + 1);
    for (int i = 0; i < len; i++)
        d[i] = aufstable[(byte)s[i]];
    d[len] = 0;
    return d;
}

void Unicode::au_fs_copy(WCHAR* dst, int maxlen, const char* src)
{
    int i;
    for (i = 0; src[i] && i < maxlen - 1; i++)
        dst[i] = aufstable[(byte)src[i]];
    dst[i] = 0;
}

void Unicode::mbtwc(UINT cp, DWORD flags, LPCSTR src, int len, LPWSTR dst, int maxlen)
{
    if (Logging::UNICODE_DEBUG)
    {
        Logger::Write(L"CP=%08X F=%x %p %02X %02X %d %p %04X %04X %d", cp, flags, src,
            (unsigned char)src[0], (unsigned char)src[1], len, dst, dst[0], dst[1], maxlen);
    }

    DWORD err = MultiByteToWideChar(cp, flags, src, len, dst, maxlen);

    if (Logging::UNICODE_DEBUG)
        Logger::Write(L"=%d %04X %04X\n", err, dst[0], dst[1]);

    if (err)
        return;

    err = GetLastError();

    Logger::Write(L"\nMBTWC %u:%d\n", cp, err);
}

void Unicode::unicode_init()
{
    //  iso-8859-15,iso-8859-1,windows-1252,default ansi
    static UINT pages[] = { 28605, 28591, 1252, 0 };
    int minac, maxac;
    UINT ac;

    for (int i = 0; fscodepage = pages[i]; i++)
    {
        if (MultiByteToWideChar(fscodepage, 0, " ", 1, nullptr, 0))
            break;
    }
    ac = GetACP();
    if (ac == 1251) // cyrillic -> always use 1251
        fscodepage = 1251;

    if (Logging::UNICODE_DEBUG)
        Logger::Write(L"Filesystem charset (ACP=%u,FSCP=%u):\n", ac, fscodepage);

    minac = 0x7f;
    maxac = 0x9f;
    for (int i = 0; i < 256; i++)
    {
        WCHAR dst1[2], dst2[2];
        char src[2];

        src[0] = i;
        src[1] = 0;
        dst1[0] = 0;
        dst1[1] = 0;
        dst2[0] = 0;
        dst2[1] = 0;
        aufstable[i] = 0;
        mbtwc(CP_ACP, 0, src, 1, dst1, 1);
        mbtwc(fscodepage, 0, src, 1, dst2, 1);

        if (Logging::UNICODE_DEBUG)
        {
            if (dst2[0] != dst1[0])
                Logger::Write(L" %02X: %04X (%04X)", i, dst1[0], dst2[0]);
            else
                Logger::Write(L" %02X: %04X       ", i, dst1[0]);
            if ((i & 3) == 3)
                Logger::Write(L"\n");
        }

        if (i < 32 || (i >= minac && i <= maxac))
            aufstable[i] = dst1[0];
        else
            aufstable[i] = dst2[0];
        if (aufstable[i] == 0)
            aufstable[i] = (unsigned char)i;
    }
    if (Logging::UNICODE_DEBUG)
        Logger::Write(L"End\n");
}

int Unicode::same_aname(const WCHAR* an1, const WCHAR* an2)
{
    return CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, an1, -1, an2, -1) == CSTR_EQUAL;
}

void Unicode::to_lower(WCHAR* s, int len)
{
    CharLowerBuff(s, len);
}

void Unicode::to_upper(WCHAR* s, int len)
{
    CharUpperBuff(s, len);
}